import { Watcher } from '../observer/watcher'
import { VNode } from '../vdom/vnode'
import { resolveSlots } from './render-helpers/index'
import { toggleObserving } from '../observer/index'

//上下文，创建当前子组件的时候用到，vdom/create-component.js
export let activeInstance = null
export function setActiveInstance(vm) {
  //上一个实例
  const prevActiveInstance = activeInstance
  //当前实例
  activeInstance = vm
  return () => {
    activeInstance = prevActiveInstance
  }
}

export function initLifecycle(vm) {
  const options = vm.$options

  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }

  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {}

  vm._watcher = null
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

export function lifecycleMixin(Vue) {
  Vue.prototype._update = function (vnode) {
    const vm = this
    const prevVnode = vm._vnode //旧的vnode
    vm._vnode = vnode //新的vnode
    const restoreActiveInstance = setActiveInstance(vm)
    if (!prevVnode) {
      vm.$el = vm.__patch__(vm.$el, vnode)
    } else {
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el
    }
  }
  Vue.prototype.$forceUpdate = function () {
    const vm = this
    if (vm._watcher) {
      vm._watcher.update()
    }
  }

}

export function mountComponent(vm, el) {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = new VNode(undefined, undefined, undefined, "")
  }
  callHook(vm, 'beforeMount')
  let updateComponent = () => {
    vm._update(vm._render())
  }
  new Watcher(vm, updateComponent, null, {
    before() {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true)
  //这里条件成立的只有根组件
  if (vm.$vnode == null) { 
    vm._isMounted = true
    //渲染完成，调用mouted钩子
    callHook(vm, 'mounted')
  }
  return vm
}


export function callHook(vm, hook) {
  const handlers = vm.$options[hook]
  handlers && handlers.call(vm)
  // if (handlers) {
  //     for (let i = 0, j = handlers.length; i < j; i++) {
  //         handlers[i].call(vm)
  //     }
  // }
}

export function updateChildComponent(
  vm, //组件的实例
  propsData,//props传值
  parentVnode, //新的vnode
  renderChildren //当前组件的children
) {


  vm.$options._parentVnode = parentVnode
  vm.$vnode = parentVnode // update vm's placeholder node without re-render

  if (vm._vnode) { // update child tree's parent
    vm._vnode.parent = parentVnode
  }
  vm.$options._renderChildren = renderChildren


  if (propsData && vm.$options.props) {
    toggleObserving(false)
    const props = vm._props
    const propKeys = vm.$options._propKeys || []
    for (let i = 0; i < propKeys.length; i++) {
      const key = propKeys[i]
      props[key] = propsData[key]
    }
    toggleObserving(true)
    vm.$options.propsData = propsData
  }

  //采用暴力递归方式对比插槽内容变化
  const needsForceUpdate = diffSlotChild(vm.$slots.default, renderChildren)

  if (needsForceUpdate) {
    //替换插槽内容
    vm.$slots = resolveSlots(renderChildren)
    //主动发起更新
    vm.$forceUpdate()
  }
}


/**
 * 对比插槽内容的变化
 * 比较条件： 1.子类长度 2.当前vnode的text值 3.tag
 * @param {Array | vnode} oldVnode 
 * @param {Array | vnode} vnode 
 */
function diffSlotChild(oldVnode, vnode){
  if(Array.isArray(oldVnode) && Array.isArray(vnode) ){ 
    if(oldVnode.length !== vnode.length){
      return true
    }else{
      for (let index = 0; index < oldVnode.length; index++) {
        const oldChild = oldVnode[index];
        const newChild = vnode[index];
        let result = diffSlotChild(oldChild, newChild)
        if(result){
          return true
        }
      }
    }
  }else if(oldVnode instanceof VNode && vnode instanceof VNode){
    if(oldVnode.children && vnode.children){
       return diffSlotChild(oldVnode.children, vnode.children)
    }else if(!oldVnode.children && !vnode.children){
      if(oldVnode.text !== vnode.text){ //普通DOM更新
        return true
      }else if(oldVnode.tag !== vnode.tag){ //插槽内存在component
        return true
      }
    }else{
      return true
    }
  }
}